﻿// -----------------------------------------------------------------------
// <copyright file="Scanner.cs" company="">
// TODO: Update copyright text.
// </copyright>
// -----------------------------------------------------------------------

namespace Scanner
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;

    /// <summary>
    /// TODO: Update summary.
    /// </summary>
    public class Scanner
    {
        DFA languageDFA = new DFA();
        public string program;
        string lexeam = "";
        int inputIndex = -1;
        int currentTokenIndex = -1;
        public List<Token> tokenList = new List<Token>();
        public Scanner()
        {
            currentTokenIndex = -1;
            languageDFA = new DFA();


            languageDFA.States[0] = new State("0");
            languageDFA.States[0].IsStart = true;
            languageDFA.States[1] = new State("1");
            languageDFA.States[2] = new State("identifer");
            languageDFA.States[2].IsFinal = true;


            languageDFA.Transitions[0].ToState = this.languageDFA.States[1];
            languageDFA.Transitions[0].type = "letter";
            languageDFA.Transitions[0].FromState = this.languageDFA.States[0];

            languageDFA.Transitions[1].FromState = this.languageDFA.States[1];
            languageDFA.Transitions[1].ToState = this.languageDFA.States[1];
            languageDFA.Transitions[1].type = "letter";

            languageDFA.Transitions[2].FromState = this.languageDFA.States[1];
            languageDFA.Transitions[2].ToState = this.languageDFA.States[1];
            languageDFA.Transitions[2].type = "digit";

            languageDFA.Transitions[3].FromState = this.languageDFA.States[1];
            languageDFA.Transitions[3].ToState = this.languageDFA.States[2];
            languageDFA.Transitions[3].type = "other_id";

            languageDFA.Transitions[4].FromState = this.languageDFA.States[0];
            languageDFA.Transitions[4].ToState = this.languageDFA.States[0];
            languageDFA.Transitions[4].type = "whiteSpace";

            //comments
            languageDFA.States[3] = new State("3");
            languageDFA.States[4] = new State("4");
            languageDFA.States[5] = new State("5");
            languageDFA.States[6] = new State("comment");
            languageDFA.States[6].IsFinal = true;

            languageDFA.Transitions[5].FromState = this.languageDFA.States[0];
            languageDFA.Transitions[5].type = "/";
            languageDFA.Transitions[5].ToState = this.languageDFA.States[3];

            languageDFA.Transitions[6].FromState = this.languageDFA.States[3];
            this.languageDFA.Transitions[6].type = "*";
            this.languageDFA.Transitions[6].ToState = this.languageDFA.States[4];

            this.languageDFA.Transitions[7].FromState = this.languageDFA.States[4];
            this.languageDFA.Transitions[7].type = "*";
            this.languageDFA.Transitions[7].ToState = this.languageDFA.States[5];

            this.languageDFA.Transitions[8].FromState = this.languageDFA.States[4];
            this.languageDFA.Transitions[8].type = "other_comment";
            this.languageDFA.Transitions[8].ToState = this.languageDFA.States[4];

            this.languageDFA.Transitions[9].FromState = this.languageDFA.States[5];
            this.languageDFA.Transitions[9].type = "other_comment";
            this.languageDFA.Transitions[9].ToState = this.languageDFA.States[4];

            this.languageDFA.Transitions[10].FromState = this.languageDFA.States[5];
            this.languageDFA.Transitions[10].type = "/";
            this.languageDFA.Transitions[10].ToState = this.languageDFA.States[6];

            //Special Symbols
            languageDFA.States[7] = new State("+");
            languageDFA.States[7].IsFinal = true;
            languageDFA.States[8] = new State("-");
            languageDFA.States[8].IsFinal = true;
            languageDFA.States[9] = new State("*");
            languageDFA.States[9].IsFinal = true;
            languageDFA.States[10] = new State("=");
            languageDFA.States[10].IsFinal = true;
            languageDFA.States[11] = new State("<");
            languageDFA.States[11].IsFinal = true;
            languageDFA.States[12] = new State("(");
            languageDFA.States[12].IsFinal = true;
            languageDFA.States[13] = new State(")");
            languageDFA.States[13].IsFinal = true;
            languageDFA.States[14] = new State(";");
            languageDFA.States[14].IsFinal = true;
            languageDFA.States[15] = new State("15");
            languageDFA.States[16] = new State(":=");
            languageDFA.States[16].IsFinal = true;

            this.languageDFA.Transitions[11].FromState = this.languageDFA.States[0];
            this.languageDFA.Transitions[11].type = "+";
            this.languageDFA.Transitions[11].ToState = this.languageDFA.States[7];

            this.languageDFA.Transitions[12].FromState = this.languageDFA.States[0];
            this.languageDFA.Transitions[12].type = "-";
            this.languageDFA.Transitions[12].ToState = this.languageDFA.States[8];

            this.languageDFA.Transitions[13].FromState = this.languageDFA.States[0];
            this.languageDFA.Transitions[13].type = "*";
            this.languageDFA.Transitions[13].ToState = this.languageDFA.States[9];

            this.languageDFA.Transitions[14].FromState = this.languageDFA.States[0];
            this.languageDFA.Transitions[14].type = "=";
            this.languageDFA.Transitions[14].ToState = this.languageDFA.States[10];

            this.languageDFA.Transitions[15].FromState = this.languageDFA.States[0];
            this.languageDFA.Transitions[15].type = "<";
            this.languageDFA.Transitions[15].ToState = this.languageDFA.States[11];

            this.languageDFA.Transitions[16].FromState = this.languageDFA.States[0];
            this.languageDFA.Transitions[16].type = "(";
            this.languageDFA.Transitions[16].ToState = this.languageDFA.States[12];

            this.languageDFA.Transitions[17].FromState = this.languageDFA.States[0];
            this.languageDFA.Transitions[17].type = ")";
            this.languageDFA.Transitions[17].ToState = this.languageDFA.States[13];

            this.languageDFA.Transitions[18].FromState = this.languageDFA.States[0];
            this.languageDFA.Transitions[18].type = ";";
            this.languageDFA.Transitions[18].ToState = this.languageDFA.States[14];

            this.languageDFA.Transitions[19].FromState = this.languageDFA.States[0];
            this.languageDFA.Transitions[19].type = ":";
            this.languageDFA.Transitions[19].ToState = this.languageDFA.States[15];

            this.languageDFA.Transitions[20].FromState = this.languageDFA.States[15];
            this.languageDFA.Transitions[20].type = "=";
            this.languageDFA.Transitions[20].ToState = this.languageDFA.States[16];

            //number

            languageDFA.States[17] = new State("17");
            languageDFA.States[18] = new State("number");
            languageDFA.States[18].IsFinal = true;


            this.languageDFA.Transitions[21].FromState = this.languageDFA.States[0];
            this.languageDFA.Transitions[21].type = "digit";
            this.languageDFA.Transitions[21].ToState = this.languageDFA.States[17];

            this.languageDFA.Transitions[22].FromState = this.languageDFA.States[17];
            this.languageDFA.Transitions[22].type = "digit";
            this.languageDFA.Transitions[22].ToState = this.languageDFA.States[17];

            this.languageDFA.Transitions[23].FromState = this.languageDFA.States[17];
            this.languageDFA.Transitions[23].type = "other_num";
            this.languageDFA.Transitions[23].ToState = this.languageDFA.States[18];

            //end of file

            this.languageDFA.States[19] = new State("\0");
            this.languageDFA.States[19].IsFinal = true;

            this.languageDFA.Transitions[24].FromState = this.languageDFA.States[0];
            this.languageDFA.Transitions[24].type = "\0";
            this.languageDFA.Transitions[24].ToState = this.languageDFA.States[19];
        }
        public char GetNextChar()
        {
            inputIndex++;
            if (inputIndex != program.Length)
            {
                lexeam = lexeam + program[inputIndex].ToString();
                return program[inputIndex];
            }
            else
                return '\0';
        }
        public Token GetNextToken()
        {
            currentTokenIndex++;
            if (currentTokenIndex != tokenList.Count)
            {
                return tokenList[currentTokenIndex];
            }
            else
                return null;
        }
        public Token GetPreviousToken()
        {
            if (currentTokenIndex == tokenList.Count)
            {
                currentTokenIndex--;
                return tokenList[currentTokenIndex];
            }
            else
            {
                currentTokenIndex --;
                return tokenList[currentTokenIndex];
            }
        }

        public char PreviousChar()
        {
            if (inputIndex == program.Length)
            {
                inputIndex--;
                return program[inputIndex];
            }
            else
            {
                inputIndex--;
                lexeam = lexeam.Substring(0, lexeam.Length - 1);
                return program[inputIndex];
            }
        }


        /// <summary>
        /// Scans the program to generate the tokens.
        /// </summary>
        /// <returns></returns>
        public bool Scan()
        {
            while (inputIndex != program.Length)
            {
                State currentState = languageDFA.States[languageDFA.StartNodeIndex];
                char currectChar = this.GetNextChar();
                while (!currentState.IsError && !currentState.IsFinal)
                {
                    currentState = languageDFA.Translate(currentState, currectChar);

                    if (!currentState.IsFinal && !currentState.IsError)
                    {
                        currectChar = this.GetNextChar();
                    }
                }

                if (currentState.IsFinal)
                {
                    if (currentState.Name == "identifer" || currentState.Name == "number")
                    {
                        this.PreviousChar();
                    }
                    bool endOfFile = Accept(currentState);
                    if (endOfFile)
                        return true;
                }
                else
                {
                    Reject();
                    return false;
                }
            }
            return true;

        }

        /// <summary>
        /// Accepts the specified final.
        /// </summary>
        /// <param name="final">The final state.</param>
        /// <returns> true if end of file</returns>
        public bool Accept(State final)
        {

            Token newToken = new Token(final.Name, lexeam);
            if (newToken.TokenType != TokenType.Comment)
            {
                tokenList.Add(newToken);
            }

            lexeam = "";
            if (newToken.TokenType == TokenType.EndFile)
                return true;
            return false;
        }
        public void Reject()
        {
            Token newToken = new Token("error", lexeam);
            tokenList.Add(newToken);
            lexeam = "";
        }

    }
}
